package com.ficus.road.maintain.ditalTwin.service;

import com.ficus.road.maintain.core.model.Coordinate;
import com.ficus.road.maintain.core.model.config.GlobalConfig;
import com.ficus.road.maintain.dao.GlobalConfigDao;
import com.ficus.road.maintain.ditalTwin.dao.AssetDao;
import com.ficus.road.maintain.ditalTwin.dao.DigitalRoadDao;
import com.ficus.road.maintain.ditalTwin.dao.FrameDao;
import com.ficus.road.maintain.core.model.digitalTwin.Asset;
import com.ficus.road.maintain.core.model.digitalTwin.DigitalRoadInfo;
import com.ficus.road.maintain.core.model.digitalTwin.Frame;
import com.ficus.road.maintain.core.model.digitalTwin.LaneInfo;
import com.ficus.road.maintain.core.model.digitalTwin.request.DigitalTwinProfileResponse;
import com.ficus.road.maintain.core.model.digitalTwin.request.ElementStatistic;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.opencsv.CSVReader;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.FileReader;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class DigitalRoadService {
    private final DigitalRoadDao roadDao;
    private final GlobalConfigDao globalConfigDao;
    private final FrameDao frameDao;

    private final AssetDao assetDao;

    private final static String CONFIG_DIGITAL_TWIN = "digital_twin_config";

    public List<DigitalRoadInfo> roadList() {
        return roadDao.findAll();
    }

    public DigitalRoadInfo getRoad(String roadId) {
        DigitalRoadInfo fetch = roadDao.fetch(roadId);
        if (fetch == null){
            return new DigitalRoadInfo();
        }
        Query query = new Query();
        query.fields().include("id");
        query.fields().include("type");
        query.fields().include("timestamp");
        query.fields().include("pic_url");
        query.fields().include("asset_mask_pic_url");
        query.fields().include("gps_result");
        fetch.setAssets(assetDao.findByQuery(query));
        return fetch;
    }

    public DigitalTwinProfileResponse profile() {
        GlobalConfig<DigitalTwinProfileResponse> one = globalConfigDao.findOne(new Query(Criteria.where("config_name")
                .is(CONFIG_DIGITAL_TWIN)));
        return one.getConfigValue();
    }

    public List<ElementStatistic> elementStatistic(String roadId) {
        Map<String, Long> collect = assetDao.findByQuery(new Query(Criteria.where("road_id").is(roadId))).stream().collect(Collectors.groupingBy(Asset::getType, Collectors.counting()));

        if (collect.isEmpty()){
            return getElementTypes().stream()
                    .map(t -> ElementStatistic.builder().type(t).artificialCount(0L)
                            .count(0L).build())
                    .collect(Collectors.toList());
        }

        GlobalConfig<DigitalTwinProfileResponse> one = globalConfigDao.findOne(new Query(Criteria.where("config_name")
                .is(CONFIG_DIGITAL_TWIN)));
        Map<String, Long> fakeElementCount = one.getConfigValue().getFakeElementCount();
        Map<String, Long> realElementCount = one.getConfigValue().getRealElementCount();
        if (realElementCount == null){
            realElementCount = Maps.newHashMap();
        }
        Map<String, Long> finalRealElementCount = realElementCount;
        return getElementTypes().stream()
                .map(t -> ElementStatistic.builder().type(t).artificialCount(fakeElementCount.getOrDefault(t, 0L))
                        .count(finalRealElementCount.containsKey(t)? finalRealElementCount.getOrDefault(t, 0L)
                                :collect.getOrDefault(t, 0L)).build())
                .collect(Collectors.toList());
    }


    public List<String> getElementTypes(){
        return Lists.newArrayList("PIC_FENCE_RESULT", "PIC_TRAFFIC_SIGN_RESULT", "PIC_MANHOLE_COVER_RESULT",
                "PIC_TRAFFIC_LIGHT_RESULT","PIC_CURBSTONE_RESULT","PIC_MILESTONE_RESULT","PIC_DUSTBIN_RESULT",
                "PIC_RED_AND_WHITE_ROD_RESULT","PIC_BOLLARD_RESULT","PIC_ISOLATION_PIER_RESULT","PIC_SPEED_BUMP_RESULT",
                "PIC_DRAIN_RESULT","PIC_EXPANSION_JOINT_RESULT","PIC_GUTTER_INLET_RESULT","PIC_BUS_STATION_RESULT");
    }


    @SneakyThrows
    public void initLane(){
        File file = new File("/home/ficus/Downloads/based_on_digital_road_landmark.csv");
        List<Frame> all = frameDao.findAll();
        CSVReader reader = new CSVReader(new FileReader(file));
        String[] line;
        reader.readNext();
        int i = 0;
        while ((line = reader.readNext()) != null) {
            String s = line[1];
            Frame frame = all.get(i++);
            LaneInfo laneInfo = new LaneInfo();
            laneInfo.setLaneWidth(Double.parseDouble(line[3]));
            laneInfo.setLaneCount(Integer.parseInt(line[2]));
            frame.setLaneInfo(laneInfo);
            frameDao.saveOrUpdate(frame);
        }
        System.out.println(1);
    }

//    @PostConstruct
    public void init() {
        initGps();
        initProfileConfig();
        initRoad();
    }

    private void initGps() {
        List<Coordinate> gpsResults = frameDao.findAll().stream()
                .map(t -> Coordinate.builder()
                        .x(String.valueOf(t.getLocation().getLongitude()))
                        .y(String.valueOf(t.getLocation().getLatitude()))
                        .build())
                .collect(Collectors.toList());
        DigitalRoadInfo one = roadDao.findOne(new Query(Criteria.where("name").is("锦绣东路")));
        one.setCoordinates(gpsResults);

        long count = assetDao.count(new Query());
        one.setAssetsCount(count);
        roadDao.saveOrUpdate(one);
    }

    private void initProfileConfig() {
        GlobalConfig one = globalConfigDao.findOne(new Query(Criteria.where("config_name").is(CONFIG_DIGITAL_TWIN)));
        if (Objects.isNull(one)) {
            GlobalConfig<DigitalTwinProfileResponse> config = new GlobalConfig<>();
            config.setConfigName(CONFIG_DIGITAL_TWIN);
            DigitalTwinProfileResponse profile = DigitalTwinProfileResponse.builder()
                    .abnormalCount(100)
                    .roadCount(4)
                    .bridgeCount(10)
                    .totalMileage(90)
                    .assetCount(200)
                    .assetTypeCount(15)
                    .abnormalCount(10)
                    .abnormalTrend(Lists.newArrayList(
                            DigitalTwinProfileResponse.Trend.builder().month("2022-10").count(51).build(),
                            DigitalTwinProfileResponse.Trend.builder().month("2022-11").count(2).build(),
                            DigitalTwinProfileResponse.Trend.builder().month("2022-12").count(9).build(),
                            DigitalTwinProfileResponse.Trend.builder().month("2023-01").count(12).build(),
                            DigitalTwinProfileResponse.Trend.builder().month("2023-02").count(25).build(),
                            DigitalTwinProfileResponse.Trend.builder().month("2023-03").count(89).build()
                    ))
                    .build();
            config.setConfigValue(profile);

            globalConfigDao.insert(config);
        }
    }

    private void initRoad() {
        List<DigitalRoadInfo> all = roadDao.findAll();
        if (!CollectionUtils.isEmpty(all)) {
            return;
        }

        DigitalRoadInfo roadInfo1 = new DigitalRoadInfo();
        roadInfo1.setName("锦绣东路");
        roadInfo1.setLaneCount(8);
        roadInfo1.setStreetType("TWO_WAY");
        roadInfo1.setHitCount(134);
        roadInfo1.setLength(11.1);
        roadInfo1.setRegion("浦东区域");
        roadInfo1.setAssetsCount(456676L);
        roadDao.insert(roadInfo1);

        DigitalRoadInfo roadInfo2 = new DigitalRoadInfo();
        roadInfo2.setName("佳京路");
        roadInfo2.setLaneCount(8);
        roadInfo2.setStreetType("TWO_WAY");
        roadInfo2.setHitCount(120);
        roadInfo2.setLength(1.1);
        roadInfo2.setRegion("佳京路");
        roadInfo2.setAssetsCount(456676L);
        roadDao.insert(roadInfo2);

        DigitalRoadInfo roadInfo3 = new DigitalRoadInfo();
        roadInfo3.setName("洲海路");
        roadInfo3.setLaneCount(8);
        roadInfo3.setStreetType("TWO_WAY");
        roadInfo3.setHitCount(98);
        roadInfo3.setLength(2.6);
        roadInfo3.setRegion("杨高北路~浦东北路");
        roadInfo3.setAssetsCount(456676L);
        roadDao.insert(roadInfo3);

        DigitalRoadInfo roadInfo4 = new DigitalRoadInfo();
        roadInfo4.setName("申江路");
        roadInfo4.setLaneCount(8);
        roadInfo4.setStreetType("TWO_WAY");
        roadInfo4.setHitCount(84);
        roadInfo4.setLength(8.1);
        roadInfo4.setRegion("五洲大道~翠柏路");
        roadInfo4.setAssetsCount(456676L);
        roadDao.insert(roadInfo4);
    }
}
